package com.yikes.yygh.service.impl;

import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yikes.yygh.common.utils.object.BeanUtils;
import com.yikes.yygh.controller.excel.DictExcel;
import com.yikes.yygh.controller.form.DictAddForm;
import com.yikes.yygh.controller.form.DictEditForm;
import com.yikes.yygh.controller.vo.DictVO;
import com.yikes.yygh.listener.DictListener;
import com.yikes.yygh.pojo.entity.Dict;
import com.yikes.yygh.mapper.DictMapper;
import com.yikes.yygh.pojo.enums.CmnServiceEnums;
import com.yikes.yygh.service.DictService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yikes.yygh.web.utils.As;
import com.yikes.yygh.web.utils.CoreUtil;
import com.yikes.yygh.web.utils.ExcelUtils;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 组织架构表 服务实现类
 * </p>
 *
 * @author guanrong.yin
 * @since 2025-05-02
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class DictServiceImpl extends ServiceImpl<DictMapper, Dict> implements DictService {

    private final RedisTemplate redisTemplate;

    @Override
    public List<DictVO> findAll() {

        //先查询redis中是否存在数据列表
        List<DictVO> tree = null;
        try {
            tree = (List<DictVO>) redisTemplate.opsForValue().get("yygh:service:cmn:dict:findAll");
            if (tree != null) {
                log.info("从redis中取值");
                return tree;
            }
        } catch (Exception e) {
            //此处不抛出异常，继续执行后面的代码
            log.error("redis服务器异常:{}", ExceptionUtils.getStackTrace(e));
        }

        log.info("从数据库中取值");
        List<Dict> list = this.list();
        List<DictVO> dictVOS = BeanUtils.toBean(list, DictVO.class);

        tree = CoreUtil.toTree(dictVOS,
                (dictVO) -> dictVO.getParentId() == 0,
                DictVO::getId,
                DictVO::getParentId,
                DictVO::setChildren);

        //将数据存入redis
        try {
            redisTemplate.opsForValue().set("yygh:service:cmn:dict:findAll", tree, 10, TimeUnit.MINUTES);
            log.info("数据存入redis");
        } catch (Exception e) {
            //此处不抛出异常，继续执行后面的代码
            log.error("redis服务器异常:{}", ExceptionUtils.getStackTrace(e));
        }

        return tree;
    }

    @Cacheable(value = "dict", keyGenerator = "keyGenerator")
    @Override
    public List<DictVO> findByParentId(Long parentId) {

        //先查询redis中是否存在数据列表
        List<DictVO> tree = null;
        try {
            tree = (List<DictVO>) redisTemplate.opsForValue().get("srb:service:dict:listByParentId" + parentId);
            if (tree != null) {
                log.info("从redis中取值");
                return tree;
            }
        } catch (Exception e) {
            //此处不抛出异常，继续执行后面的代码
            log.error("redis服务器异常：" + ExceptionUtils.getStackTrace(e));
        }

        log.info("从数据库中取值");
        // 获取数据列表
        List<Dict> list = baseMapper.selectList(lambdaQuery().eq(Dict::getParentId, parentId));
        List<DictVO> beanList = BeanUtils.toBean(list, DictVO.class);
        beanList.forEach(dict -> {
            //如果有子节点，则是非叶子节点
            boolean hasChildren = this.isChildren(dict.getId());
            dict.setHasChildren(hasChildren);
        });

        // 三级树-数据
        tree = CoreUtil.toTree(beanList, dict -> dict.getParentId().equals(0L), DictVO::getId, DictVO::getParentId, DictVO::setChildren);

        //将数据存入redis
        try {
            redisTemplate.opsForValue().set("srb:core:dictList:" + parentId, tree, 5, TimeUnit.MINUTES);
            log.info("数据存入redis");
        } catch (Exception e) {
            //此处不抛出异常，继续执行后面的代码
            log.error("redis服务器异常：" + ExceptionUtils.getStackTrace(e));
        }

        return tree;
    }

    @Override
    public void exportData(HttpServletResponse response) {

        List<Dict> list = this.list();
        List<DictExcel> dictExcels = new ArrayList<>(list.size());

        for (Dict dict : list) {
            DictExcel bean = BeanUtils.toBean(dict, DictExcel.class);
            dictExcels.add(bean);
        }

        ZonedDateTime zonedDateTime = LocalDateTime.now().atZone(ZoneId.of("Asia/Shanghai"));
        Long timestamp = zonedDateTime.toInstant().toEpochMilli();
        String sheetName = "数据字典模板_" + timestamp;

        ExcelUtils.excelExport(dictExcels, sheetName, sheetName, DictExcel.class, response);
    }

    @CacheEvict(value = "dict", allEntries = true)
    @Override
    public void importData(MultipartFile file) {

        try {
            EasyExcel.read(file.getInputStream(), DictExcel.class, new DictListener(baseMapper)).sheet().doRead();

        } catch (Exception e) {
            log.error("导入数据字典失败：{}", e.getMessage());
            throw new RuntimeException(e);
        }
    }

    @Override
    public DictVO getInfoId(Long id) {

        DictVO bean = BeanUtils.toBean(getById(id), DictVO.class);

        boolean children = this.isChildren(id);
        bean.setHasChildren(children);

        return bean;
    }

    @Override
    public void add(DictAddForm form) {

        Dict bean = BeanUtils.toBean(form, Dict.class);

        save(bean);
    }

    @Override
    public void edit(DictEditForm form) {

        Dict bean = BeanUtils.toBean(form, Dict.class);
        updateById(bean);
    }

    @Override
    public void del(Long id) {

        boolean children = this.isChildren(id);
        As.isTure(children, CmnServiceEnums.IS_PARENT_IS_DATA);

        removeById(id);
    }

    @Override
    public void exportTemplate(HttpServletResponse response) {

        ZonedDateTime zonedDateTime = LocalDateTime.now().atZone(ZoneId.of("Asia/Shanghai"));
        Long timestamp = zonedDateTime.toInstant().toEpochMilli();

        String sheetName = "数据字典模板_" + timestamp;
        ExcelUtils.excelExport(Lists.newArrayList(), sheetName, sheetName, DictExcel.class, response);
    }

    @Cacheable(value = "dict", keyGenerator = "keyGenerator")
    @Override
    public String getNameByParentDictCodeAndValue(String parentDictCode, String value) {
        // 如果value能唯一定位数据字典，parentDictCode可以传空，例如：省市区的value值能够唯一确定
        if (StrUtil.isEmpty(parentDictCode)) {

            Dict dict = baseMapper.selectOne(new LambdaQueryWrapper<Dict>().eq(Dict::getValue, value));

            if (Objects.nonNull(dict)) {
                return dict.getName();
            }
        } else {
            Dict parentDict = this.getByDictsCode(parentDictCode);
            if (Objects.isNull(parentDict )) {
                return "";
            }

            Dict dict = baseMapper.selectOne(new LambdaQueryWrapper<Dict>().eq(Dict::getParentId, parentDict.getId())
                    .eq(Dict::getValue, value));
            if (Objects.nonNull(dict)) {
                return dict.getName();
            }
        }
        return "";

    }

    /**
     * 根据dictCode查询
     * @param parentDictCode
     * @return
     */
    private Dict getByDictsCode(String parentDictCode) {

        Dict dict = baseMapper.selectOne(new LambdaQueryWrapper<Dict>().eq(Dict::getDictCode, parentDictCode));
        return dict;
    }

    /**
     * 判断id下面是否有子节点
     *
     * @param id
     * @return
     */
    private boolean isChildren(Long id) {

        Long count = lambdaQuery().eq(Dict::getParentId, id).count();
        // 0>0    1>0
        return count > 0;
    }
}
